/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <stdio.h>

#include "rdx.h"
#include "rdx_objectmanagement.hpp"
#include "rdx_programmability.hpp"
#include "rdx_hashmap.hpp"
#include "rdx_io.hpp"
#include "rdx_lut.hpp"

namespace RDX
{
	namespace ObjectManagement
	{
		using namespace RDX::Collections;
		using namespace RDX::IO;
		using namespace RDX::Programmability;
		using namespace RDX::ObjectManagement;
		using namespace RDX::Utility;

		struct PackageProcessor : public ITypeProcessor
		{
			void Finalize(void *obj, IObjectManager *objm) const
			{
			}

			bool OnLoad(OperationContext *ctx, void *obj, IObjectManager *objm) const
			{
				return true;
			}

			void MarkDependencies(IObjectManager *objm, void *obj, const StructuredType *st, bool markNative, ISerializer *ser, ScanID scanID, GCLink gcl) const
			{
				if(!markNative)
					return;

				Package *p = static_cast<Package*>(obj);
				objm->SerializeIncludeObject(ser, p->_native.importedSymbols, gcl);
				objm->SerializeIncludeObject(ser, p->_native.localSymbols, gcl);
				objm->SerializeIncludeObject(ser, p->_native.strings, gcl);
				objm->SerializeIncludeObject(ser, p->_native.firstObject, gcl);

				Package::ManifestImport *imps = p->_native.importedSymbols;
				if(imps)
				{
					for(LargeInt i=0;i<GCInfo::From(imps)->numElements;i++)
					{
						objm->SerializeIncludeObject(ser, imps[i].ref.InterpretAs<void>(), gcl);
						objm->SerializeIncludeObject(ser, imps[i].symbolName, gcl);
					}
				}

				Package::ManifestLocal *ls = p->_native.localSymbols;
				if(ls)
				{
					for(LargeInt i=0;i<GCInfo::From(ls)->numElements;i++)
					{
						objm->SerializeIncludeObject(ser, ls[i].ref.InterpretAs<void>(), gcl);
						objm->SerializeIncludeObject(ser, ls[i].symbolName, gcl);
						objm->SerializeIncludeObject(ser, ls[i].tempTypeName, gcl);
						objm->SerializeIncludeObject(ser, ls[i].resolvedType, gcl);
						if(ls[i].typeRef)
							objm->SerializeIncludeObject(ser, ls[i].typeRef->InterpretAs<void>(), gcl);
					}
				}
			}
			static PackageProcessor instance;
		};
		PackageProcessor PackageProcessor::instance;

		class TextFileParser : public ITextDeserializer
		{
		public:
			static const LargeInt BUFFER_SIZE = 1024;
			static const LargeInt BUFFER_FRONTLOAD = 1024;
			static const LargeInt STATIC_STRING_SIZE = 1024;

		private:
			unsigned char _backlog[BUFFER_SIZE+BUFFER_FRONTLOAD];
			unsigned char *_debugLocalized;
			LargeInt _bufferOffset;
			LargeInt _position;
			LargeInt _backlogAvailable;

			IO::IFileStream *_stream;

			void RefillBacklog()
			{
				if(_backlogAvailable < BUFFER_FRONTLOAD)
				{
					if(_backlogAvailable)
						memmove(_backlog, _backlog + _bufferOffset, static_cast<size_t>(_backlogAvailable));
					_bufferOffset = 0;
					_backlogAvailable += _stream->ReadBytes(_backlog + _backlogAvailable, BUFFER_SIZE+BUFFER_FRONTLOAD - _backlogAvailable);
				}
			}

		public:
			LargeInt WriteBytes(const void *src, LargeInt numBytes)
			{
				return _stream->WriteBytes(src, numBytes);
			}

			LargeInt ReadBytes(void *dest, LargeInt numBytes)
			{
				return _stream->ReadBytes(dest, numBytes);
			}

			void Close()
			{
				// This doesn't close because it's on top of a separately-managed stream
			}

			LargeInt Tell()
			{
				return _position;
			}

			LargeInt PeekBytes(Byte *dest, LargeInt count)
			{
				if(_backlogAvailable < count)
					return PeekBytes(dest, _backlogAvailable);

				BlockCopy(dest, _backlog + _bufferOffset, static_cast<size_t>(count));

				return count;
			}
			
			void Seek(LargeInt offset, SeekType seekType)
			{
				_stream->Seek(offset, seekType);
				_position = offset;
				_backlogAvailable = 0;
				_bufferOffset = 0;
				RefillBacklog();
			}

			LargeInt ReadBytes(unsigned char *dest, LargeInt count)
			{
				LargeInt sz = PeekBytes(dest, count);

				_backlogAvailable -= sz;
				_bufferOffset += sz;

				if(sz < count)
					sz = sz + _stream->ReadBytes(dest + sz, count - sz);
				_position += sz;

				RefillBacklog();

				return sz;
			}

			
			static bool IsPunctuation(Byte b)
			{
				return b == ':' || b == '{' || b == '}' || b == ',';
			}

			static bool IsWhitespace(Byte b)
			{
				return b <= ' ';
			}

			bool CheckToken(const char *str)
			{
				LargeInt len = static_cast<LargeInt>(strlen(str));

				_debugLocalized = _backlog + _bufferOffset;
				if(_backlogAvailable >= len && !memcmp(_backlog + _bufferOffset, str, static_cast<size_t>(len)) &&
					(
						(_backlogAvailable == len || IsWhitespace(_backlog[_bufferOffset + len]) || IsPunctuation(_backlog[_bufferOffset + len])
					)))
					return true;
				return false;
			}

			static void FlushBytes(OperationContext *ctx, IObjectManager *om, const Byte *staticString, LargeInt appendLength, CRef<Byte>* pbytes)
			{
				LargeInt currentCapacity = 0;
				if(*pbytes)
					currentCapacity = GCInfo::From(*pbytes)->dimensions[0];

				CRef<Byte> newBytes = om->Create1DArray<Byte>(ctx, currentCapacity + appendLength);
				if(currentCapacity)
					BlockCopy(newBytes, *pbytes, static_cast<size_t>(currentCapacity + appendLength));
				if(appendLength)
					BlockCopy(newBytes + currentCapacity, staticString, static_cast<size_t>(appendLength));
				*pbytes = newBytes;
			}

			CRef<const String> ParseToken(OperationContext *ctx, IObjectManager *om, bool *pIsString, bool shouldReturn = true)
			{
				Byte staticString[STATIC_STRING_SIZE];
				CRef<Byte> bytes = NULL;
				UInt8 b;
				bool parseAsString = false;

				RDX_TRY(ctx)
				{
					while(1)
					{
						if(!PeekBytes(&b, 1))
							RDX_LTHROWV(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED, NULL);
						if(!IsWhitespace(b))
							break;
						ReadBytes(&b, 1);
					}

					PeekBytes(&b, 1);
					if(b == '\'')
					{
						parseAsString = true;
						ReadBytes(&b, 1);
					}

					LargeInt loadedStatic = 0;
					bool firstCharacter = true;

					while(1)
					{
						if(!PeekBytes(&b, 1))
						{
							if(parseAsString)
								RDX_LTHROWV(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED, NULL);
							break;
						}

						if(parseAsString && b == '\'')
						{
							ReadBytes(&b, 1);
							break;
						}
						else if(!parseAsString && (IsWhitespace(b) || (IsPunctuation(b) && !firstCharacter)))
							break;

						firstCharacter = false;

						if(b == '\\')
						{
							unsigned char sequence[3];
							if(!PeekBytes(sequence, 2))
								RDX_LTHROWV(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED, NULL);

							if(sequence[1] == '\\' || sequence[1] == '\'')
							{
								b = sequence[1];
								ReadBytes(sequence, 2);
							}
							else if(sequence[1] >= '0' && sequence[1] <= '9')
							{
								if(!PeekBytes(sequence, 3))
									RDX_LTHROWV(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED, NULL);
								if(sequence[2] < '0' || sequence[2] > '9')
									RDX_LTHROWV(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED, NULL);
								b = static_cast<UInt8>((sequence[1]-'0')*10 + (sequence[2]-'0'));
								ReadBytes(sequence, 3);
							}
							else
								RDX_LTHROWV(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED, NULL);
						}
						else
							ReadBytes(&b, 1);

						if(shouldReturn)
						{
							if(loadedStatic == STATIC_STRING_SIZE)
							{
								RDX_PROTECT(ctx, FlushBytes(ctx, om, staticString, loadedStatic, &bytes) );
								loadedStatic = 0;
							}

							staticString[loadedStatic++] = b;
						}
					}

					// Skip further whitespace
					while(1)
					{
						if(!PeekBytes(&b, 1))
							break;
						if(!IsWhitespace(b))
							break;
						ReadBytes(&b, 1);
					}

					*pIsString = parseAsString;

					if(shouldReturn)
					{
						CRef<const String> str;
						if(bytes)
						{
							RDX_PROTECT(ctx, FlushBytes(ctx, om, staticString, loadedStatic, &bytes) );
							RDX_PROTECT_ASSIGN(ctx, str, om->CreateStringUTF8(ctx, bytes, GCInfo::From(bytes)->dimensions[0]) );
						}
						else
							RDX_PROTECT_ASSIGN(ctx, str, om->CreateStringUTF8(ctx, staticString, loadedStatic) );
						
						return str;
					}
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROWV(ctx, NULL);
				}
				RDX_ENDTRY

				return NULL;
			}

			void SkipToken(OperationContext *ctx)
			{
				bool b;
				ParseToken(ctx, NULL, &b, false);
			}

			explicit TextFileParser(IFileStream *stream)
			{
				_stream = stream;
			}

			bool HasAborted() const
			{
				return _stream->HasAborted();
			}

			void Abort()
			{
				_stream->Abort();
			}
		};

		class ManifestImportProcessor : public ITypeProcessor
		{
			void Finalize(void *obj, IObjectManager *objm) const
			{
			}

			bool OnLoad(OperationContext *ctx, void *obj, IObjectManager *objm) const
			{
				return true;
			}

			void MarkDependencies(IObjectManager *objm, void *obj, const StructuredType *st, bool markNative, ISerializer *ser, ScanID scanID, GCLink gcl) const
			{
				if(!markNative)
					return;

				Package::ManifestImport *mi = static_cast<Package::ManifestImport*>(obj);
				objm->SerializeIncludeObject(ser, mi->ref.InterpretAs<void>(), gcl);
				objm->SerializeIncludeObject(ser, mi->symbolName, gcl);
			}

		public:
			static ManifestImportProcessor instance;
		};
		ManifestImportProcessor ManifestImportProcessor::instance;

		class ManifestLocalProcessor : public ITypeProcessor
		{
			void Finalize(void *obj, IObjectManager *objm) const
			{
			}

			bool OnLoad(OperationContext *ctx, void *obj, IObjectManager *objm) const
			{
				return true;
			}

			void MarkDependencies(IObjectManager *objm, void *obj, const StructuredType *st, bool markNative, ISerializer *ser, ScanID scanID, GCLink gcl) const
			{
				if(!markNative)
					return;

				Package::ManifestLocal *ml = static_cast<Package::ManifestLocal*>(obj);
				objm->SerializeIncludeObject(ser, ml->ref.InterpretAs<void>(), gcl);
				objm->SerializeIncludeObject(ser, ml->symbolName, gcl);
				objm->SerializeIncludeObject(ser, ml->tempTypeName, gcl);
				objm->SerializeIncludeObject(ser, ml->resolvedType, gcl);
				if(ml->typeRef)
					objm->SerializeIncludeObject(ser, ml->typeRef->InterpretAs<void>(), gcl);
			}

		public:
			static ManifestLocalProcessor instance;
		};
		ManifestLocalProcessor ManifestLocalProcessor::instance;

		void ParseTextManifest(OperationContext *ctx, IObjectManager *objManager, IFileStream *stream,
			Package *pkg)
		{
			bool errored = false;
			int eCode = 0;

			CRef<const String> str1 = NULL;
			CRef<const String> str2 = NULL;

			LargeInt numCriticalResources = 0;
			LargeInt numNonCriticalResources = 0;
			LargeInt numResources = 0;
			LargeInt numImports = 0;
			bool isString;

			HashMap<HashMapSimple<const String*, LoadShell> > *localResources = static_cast<HashMap<HashMapSimple<const String*, LoadShell> > *>(objManager->GetLocalResourcesMap());
			HashMap<HashMapSimple<const String*, LoadShell> > *importedResources = static_cast<HashMap<HashMapSimple<const String*, LoadShell> > *>(objManager->GetImportedResourcesMap());

			RDX_TRY(ctx)
			{
				TextFileParser tfp(stream);
				tfp.Seek(0, IFileStream::SEEK_Start);
				UInt8 b;

				while(tfp.PeekBytes(&b, 1))
				{
					if(tfp.CheckToken("import"))
					{
						RDX_PROTECT(ctx, tfp.SkipToken(ctx) );
						RDX_PROTECT_ASSIGN(ctx, str1, tfp.ParseToken(ctx, objManager, &isString) );
						if(str1 == NULL)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						
						LoadShell shell;
						shell.tableIndex = numImports++;
						shell.typeName = NULL;
						const String *drStr = str1;
						RDX_PROTECT(ctx, importedResources->Insert(ctx, &drStr, &shell) );
						
						str1 = NULL;
					}
					else if(tfp.CheckToken("def"))
					{
						RDX_PROTECT(ctx, tfp.SkipToken(ctx) );
						RDX_PROTECT_ASSIGN(ctx, str1, tfp.ParseToken(ctx, objManager, &isString) );
						if(str1 == NULL)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						RDX_PROTECT_ASSIGN(ctx, str2, tfp.ParseToken(ctx, objManager, &isString) );
						if(str2 == NULL)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						
						LoadShell shell;
						shell.isAnonymous = false;
						shell.isConstant = false;

						while(!isString)
						{
							if(str2->Equal("anonymous"))
								shell.isAnonymous = true;
							else if(str2->Equal("const"))
								shell.isConstant = true;
							else
								RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
							
							RDX_PROTECT_ASSIGN(ctx, str2, tfp.ParseToken(ctx, objManager, &isString) );
							if(str2 == NULL)
								RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						}

						shell.fileOffset = tfp.Tell();
						shell.typeName = str2;
						shell.tableIndex = numResources++;

						CRef<void> type = objManager->LookupSymbolSimple(ctx, str2);
						
						CRef<const String> drStrRef = str1;	// So that this is counted if Insert collects
						const String *drStr = str1;
						RDX_PROTECT(ctx, localResources->Insert(ctx, &drStr, &shell) );

						str1 = NULL;
						str2 = NULL;
					}
					else
						RDX_PROTECT(ctx, tfp.SkipToken(ctx) );
				}

				RDX_PROTECT_ASSIGN(ctx, pkg->_native.localSymbols, objManager->Create1DArray<Package::ManifestLocal>(ctx, numResources, NULL, &ManifestLocalProcessor::instance, pkg->_native.domain) );
				RDX_PROTECT_ASSIGN(ctx, pkg->_native.importedSymbols, objManager->Create1DArray<Package::ManifestImport>(ctx, numImports, NULL, &ManifestImportProcessor::instance, pkg->_native.domain) );

				// Create manifest symbols
				if(localResources->_native.untracedEntries)
				{
					for(LargeInt i=0;i<localResources->_native.numEntries;i++)
					{
						HashEntry<const String*, LoadShell> *e = localResources->_native.untracedEntries + i;

						if(e->e.elementType > HashElement::HMT_ContainsData)
						{
							Package::ManifestLocal ml;

							ml.fileOffset = e->v.fileOffset;
							ml.flags = 0;

							ml.ref.targetType = TargetableObject::TT_Shell;
							ml.ref.resolution = NULL;
							ml.symbolName = e->k;
							ml.tempTypeName = e->v.typeName;
							ml.typeRef = NULL;
							ml.resolvedType = NULL;

							if(e->v.isAnonymous)
								ml.flags |= Package::ManifestLocal::PMLF_Anonymous;
							if(e->v.isConstant)
								ml.flags |= Package::ManifestLocal::PMLF_Constant;

							LargeInt idx = e->v.tableIndex;
							pkg->_native.localSymbols[idx] = ml;
							e->v.ref = &pkg->_native.localSymbols[idx].ref;
						}
					}
				}

				if(importedResources->_native.untracedEntries)
				{
					for(LargeInt i=0;i<importedResources->_native.numEntries;i++)
					{
						HashEntry<const String*, LoadShell> *e = importedResources->_native.untracedEntries + i;

						if(e->e.elementType > HashElement::HMT_ContainsData)
						{
							Package::ManifestImport mi;

							mi.ref.targetType = TargetableObject::TT_Shell;
							mi.ref.resolution = NULL;
							mi.symbolName = e->k;

							LargeInt idx = e->v.tableIndex;
							pkg->_native.importedSymbols[idx] = mi;
							e->v.ref = &pkg->_native.importedSymbols[idx].ref;
						}
					}
				}

				LargeInt nLocalSymbols = GCInfo::From(pkg->_native.localSymbols)->numElements;
				for(LargeInt i=0;i<nLocalSymbols;i++)
				{
					Package::ManifestLocal *ml = pkg->_native.localSymbols + i;
					LoadShell shell;

					// Match the type name
					ml->resolvedType = NULL;
					if(importedResources->GetElement(&ml->tempTypeName, &shell))
						ml->typeRef = shell.ref;
					else if(localResources->GetElement(&ml->tempTypeName, &shell))
						ml->typeRef = shell.ref;
					else
						RDX_STHROW(ctx, Errors::RDX_ERROR_UNRESOLVED_SYMBOL_REFERENCE);
				}

				localResources->Clear();
				importedResources->Clear();
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}

		struct BinaryHeader
		{
			char magic[16];
			LargeInt numImports;
			LargeInt numLocalSymbols;
			LargeInt numStrings;
		};

		struct BinaryImport
		{
			int a;
		};

		static LargeInt ReadBinaryLargeInt(OperationContext *ctx, IO::IFileStream *fs)
		{
			LargeInt li = 0;
			bool overflowed = false;
			bool readFailed = true;
			if(!fs->ReadConverted<BinPackageLargeInt, LargeInt>(&li, overflowed, readFailed))
			{
				if(overflowed)
					RDX_LTHROWV(ctx, Errors::RDX_ERROR_INTEGER_OVERFLOW, static_cast<LargeInt>(0));
				if(readFailed)
					RDX_LTHROWV(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED, static_cast<LargeInt>(0));
			}
			return li;
		}

		void ParseBinaryManifest(OperationContext *ctx, IObjectManager *objManager, IFileStream *stream,
			Package *pkg)
		{
			bool errored = false;
			int eCode = 0;

			LargeInt numCriticalResources = 0;
			LargeInt numNonCriticalResources = 0;
			LargeInt numResources = 0;
			LargeInt numExports = 0;
			LargeInt numImports = 0;

			RDX_TRY(ctx)
			{
				BinaryHeader header;
				{
					if(stream->ReadBytes(header.magic, 16) != 16)
						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
					RDX_PROTECT_ASSIGN(ctx, header.numImports, ReadBinaryLargeInt(ctx, stream));
					RDX_PROTECT_ASSIGN(ctx, header.numLocalSymbols, ReadBinaryLargeInt(ctx, stream));
					RDX_PROTECT_ASSIGN(ctx, header.numStrings, ReadBinaryLargeInt(ctx, stream));
				}

				if(memcmp(header.magic, RDX_MAGIC, 16) ||
					header.numImports < 0 ||
					header.numLocalSymbols < 0 ||
					header.numStrings < 0)
					RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

				RDX_PROTECT_ASSIGN(ctx, pkg->_native.importedSymbols, objManager->Create1DArray<Package::ManifestImport>(ctx, header.numImports));
				RDX_PROTECT_ASSIGN(ctx, pkg->_native.localSymbols, objManager->Create1DArray<Package::ManifestLocal>(ctx, header.numLocalSymbols));

				// Set all manifest symbols to shells
				for(LargeInt i=0;i<header.numImports;i++)
					pkg->_native.importedSymbols[i].ref.targetType = TargetableObject::TT_Shell;
				for(LargeInt i=0;i<header.numLocalSymbols;i++)
					pkg->_native.localSymbols[i].ref.targetType = TargetableObject::TT_Shell;

				ArrayOfType *aot_String;
				RDX_PROTECT_ASSIGN(ctx, aot_String, objManager->CreateArrayType(ctx, objManager->GetBuiltIns()->st_String, 1, true));
				RDX_PROTECT_ASSIGN(ctx, pkg->_native.strings, objManager->Create1DArray<const String*>(ctx, header.numStrings, aot_String));
				
				const ITypeSerializer *strs = objManager->GetBuiltIns()->st_String->_native.user.typeSerializer;

				// Read strings
				for(LargeInt i=0;i<header.numStrings;i++)
				{
					UInt8 characters[1024];
					LargeInt nUTF8Bytes = 0;

					RDX_PROTECT_ASSIGN(ctx, nUTF8Bytes, ReadBinaryLargeInt(ctx, stream));
					if(nUTF8Bytes < 0)
						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

					if(nUTF8Bytes <= sizeof(characters))
					{
						if(stream->ReadBytes(characters, nUTF8Bytes) != nUTF8Bytes)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

						RDX_PROTECT_ASSIGN(ctx, pkg->_native.strings[i], objManager->CreateStringUTF8(ctx, characters, nUTF8Bytes));
					}
					else
					{
						CRef<UInt8> tempChars;
						RDX_PROTECT_ASSIGN(ctx, tempChars, objManager->Create1DArray<UInt8>(ctx, nUTF8Bytes));

						if(stream->ReadBytes(tempChars, nUTF8Bytes) != nUTF8Bytes)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

						RDX_PROTECT_ASSIGN(ctx, pkg->_native.strings[i], objManager->CreateStringUTF8(ctx, tempChars, nUTF8Bytes));
					}
				}

				for(LargeInt i=0;i<header.numImports;i++)
				{
					CRef<const String> str;

					Package::ManifestImport *mi = pkg->_native.importedSymbols + i;
					
					// Deserialize name
					RDX_PROTECT_ASSIGN(ctx, mi->symbolName, objManager->DeserializeBinaryString(ctx, stream, pkg) );
					if(mi->symbolName == NULL)
						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
					mi->ref.targetType = TargetableObject::TT_Shell;
					mi->ref.resolution = NULL;
				}
				
				for(LargeInt i=0;i<header.numLocalSymbols;i++)
				{
					Package::ManifestLocal *ml = pkg->_native.localSymbols + i;
					RDX_PROTECT_ASSIGN(ctx, ml->fileOffset, ReadBinaryLargeInt(ctx, stream));
					if(ml->fileOffset < 0)
						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

					RDX_PROTECT_ASSIGN(ctx, ml->symbolName, objManager->DeserializeBinaryString(ctx, stream, pkg) );
					if(ml->symbolName == NULL)
						ml->flags |= Package::ManifestLocal::PMLF_Anonymous;
					
					LargeInt typeSymbolID = 0;
					RDX_PROTECT_ASSIGN(ctx, typeSymbolID, ReadBinaryLargeInt(ctx, stream));

					if(typeSymbolID == 0)
						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
					else if(typeSymbolID < 0)
					{
						typeSymbolID = (-typeSymbolID) - 1;
						if(typeSymbolID >= header.numImports)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						ml->typeRef = &pkg->_native.importedSymbols[typeSymbolID].ref;
					}
					else
					{
						typeSymbolID--;
						if(typeSymbolID >= header.numLocalSymbols)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						ml->typeRef = &pkg->_native.localSymbols[typeSymbolID].ref;
					}

					ml->flags = 0;
					stream->ReadSwappableBytes(&ml->flags, sizeof(ml->flags));
					ml->flags &= (0);	// TODO: No valid flags, currently...
					ml->ref.targetType = TargetableObject::TT_Shell;
					ml->ref.resolution = NULL;
				}
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}

		void DeserializeReference(OperationContext *ctx, IObjectManager *objManager, Package *pkg,
			Domain domain, IPackageHost *host, IFileStream *fs, bool isText, const Type *t, void **ref)
		{
			RDX_TRY(ctx)
			{
				if(isText)
				{
					ITextDeserializer *tfp = static_cast<ITextDeserializer*>(fs);
					if(tfp->CheckToken("null"))
					{
						*ref = NULL;
						RDX_PROTECT(ctx, tfp->SkipToken(ctx) );

						return;
					}
					if(tfp->CheckToken("res"))
					{
						bool isString;
		
						CRef<const String> resName;

						RDX_PROTECT(ctx, tfp->SkipToken(ctx) );
						RDX_PROTECT_ASSIGN(ctx, resName, tfp->ParseToken(ctx, objManager, &isString, true) );

						if(!isString)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

						LargeInt nRefs = GCInfo::From(pkg->_native.importedSymbols)->numElements;
						for(LargeInt i=0;i<nRefs;i++)
						{
							if(pkg->_native.importedSymbols[i].symbolName == resName)
							{
								*ref = pkg->_native.importedSymbols[i].ref.InterpretAs<void>();
								return;
							}
						}

						nRefs = GCInfo::From(pkg->_native.localSymbols)->numElements;
						for(LargeInt i=0;i<nRefs;i++)
						{
							if(pkg->_native.localSymbols[i].symbolName == resName)
							{
								*ref = pkg->_native.localSymbols[i].ref.InterpretAs<void>();
								return;
							}
						}

						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
					}

					// Try to parse this as a string.  This is destructive, so any other parse attempts will need to reset the stream
					CRef<const String> str;
					bool isString;
					RDX_PROTECT_ASSIGN(ctx, str, tfp->ParseToken(ctx, objManager, &isString, true) );

					if(!isString)
						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
					*ref = const_cast<String *>(str.Object());
					return;
				}
				else
				{
					// Binary
					LargeInt symbolIndex;
					RDX_PROTECT_ASSIGN(ctx, symbolIndex, ReadBinaryLargeInt(ctx, fs));
					if(symbolIndex == 0)
					{
						*ref = NULL;
					}
					else if(symbolIndex < 0)
					{
						// Imported
						symbolIndex = (-symbolIndex)-1;

						if(symbolIndex >= GCInfo::From(pkg->_native.importedSymbols)->numElements)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						*ref = pkg->_native.importedSymbols[symbolIndex].ref.InterpretAs<void>();
					}
					else
					{
						// Exported
						symbolIndex--;
						if(symbolIndex >= GCInfo::From(pkg->_native.localSymbols)->numElements)
						{
							// Could be from the string table
							symbolIndex -= GCInfo::From(pkg->_native.localSymbols)->numElements;
							if(symbolIndex >= GCInfo::From(pkg->_native.strings)->numElements)
								RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
							*ref = const_cast<String *>(pkg->_native.strings[symbolIndex]);
							return;
						}
						*ref = pkg->_native.localSymbols[symbolIndex].ref.InterpretAs<void>();
					}
				}
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}

		void DeserializeBulk(OperationContext *ctx, IObjectManager *objManager, Package *pkg,
			Domain domain, IPackageHost *host, IFileStream *fs, const StructuredType *st, void *output, LargeInt numElements, int depth);

		void DeserializeArray(OperationContext *ctx, IObjectManager *objManager, Package *pkg,
			Domain domain, IPackageHost *host, IFileStream *fs, bool isText, ArrayOfType *aot, CRef<void> &aref)
		{
			RDX_TRY(ctx)
			{
				bool isString;
				LargeInt staticDimensions[RDX_MAX_DIMENSIONS];
				CRef<LargeInt> ddimensions;
				LargeInt *dimensions;
				LargeInt totalElements;
				if(aot->numDimensions > RDX_MAX_DIMENSIONS)
				{
					RDX_PROTECT_ASSIGN(ctx, ddimensions, objManager->Create1DArray<LargeInt>(ctx, aot->numDimensions));
					dimensions = ddimensions;
				}
				else
					dimensions = staticDimensions;

				// Read in dimensions
				totalElements = 1;

				const ITypeSerializer *lis = objManager->GetBuiltIns()->st_LargeInt->_native.user.typeSerializer;
				for(LargeInt i=0;i<aot->numDimensions;i++)
				{
					LargeInt d;

					if(isText)
					{
						RDX_PROTECT(ctx, lis->DeserializeTextInstance(ctx, objManager, &d, static_cast<ITextDeserializer*>(fs), host, pkg) );
					}
					else
					{
						RDX_PROTECT(ctx, lis->DeserializeBinaryInstance(ctx, objManager, &d, fs, host, pkg) );
					}

					if(d < 0)
						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

					dimensions[i] = d;

					if(!CheckMulOverflow(totalElements, d))
						RDX_STHROW(ctx, Errors::RDX_ERROR_INTEGER_OVERFLOW);

					totalElements *= d;
				}

				Type *subType = aot->type;
				LargeInt elementSize = 0;

				if(!subType)
					RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

				elementSize = objManager->TypeElementSize(aot);
				if(!elementSize)
					RDX_STHROW(ctx, Errors::RDX_ERROR_INTERNAL_BAD_TYPE);


				CRef<void> objRef;
				RDX_PROTECT_ASSIGN(ctx, objRef, objManager->CreateContainer(ctx, elementSize, totalElements, aot->numDimensions, domain, aot, NULL, 0) );
				for(LargeInt i=0;i<aot->numDimensions;i++)
					GCInfo::From(objRef)->dimensions[i] = dimensions[i];
				if(domain != DOMAIN_Runtime && domain != DOMAIN_Duplicable)
					GCInfo::From(objRef)->package = pkg;

				RDX_PROTECT(ctx, objManager->AddUnloadedObject(ctx, objRef));

				UInt8 *byteBase = reinterpret_cast<UInt8*>(static_cast<void*>(objRef));
				
				// Parse out elements
				if(isText)
				{
					ITextDeserializer *tfp = static_cast<ITextDeserializer*>(fs);
					if(!tfp->CheckToken("{")) RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
					RDX_PROTECT(ctx, tfp->SkipToken(ctx));
				}


				if(isText == false
					&& (GCInfo::From(subType)->containerType == objManager->GetBuiltIns()->st_StructuredType)
					&& (static_cast<const StructuredType *>(subType)->_native.flags & StructuredType::NativeProperties::STF_AllowBulkSerialize)
					&& !objManager->TypeIsObjectReference(subType))
				{
					// Bulk serializable values
					RDX_PROTECT(ctx, DeserializeBulk(ctx, objManager, pkg, domain, host, fs, static_cast<const StructuredType *>(subType), byteBase, totalElements, 0));
				}
				else
				{
					for(LargeInt i=0;i<totalElements;i++)
					{
						RDX_PROTECT(ctx, DeserializeValue(ctx, objManager, pkg, domain, host, fs, isText, aot->type, byteBase, 0));
						byteBase += elementSize;

						if(isText)
						{
							ITextDeserializer *tfp = static_cast<ITextDeserializer*>(fs);
							if( tfp->CheckToken(",") )
								tfp->ParseToken(ctx, NULL, &isString, false);
							else if(i != totalElements-1)
								RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						}
					}
				}

				if(isText)
				{
					ITextDeserializer *tfp = static_cast<ITextDeserializer*>(fs);
					if(!tfp->CheckToken("}")) RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
					RDX_PROTECT(ctx, tfp->SkipToken(ctx));
				}

				aref = objRef;
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}

		void DeserializeBulk(OperationContext *ctx, IObjectManager *objManager, Package *pkg,
			Domain domain, IPackageHost *host, IFileStream *fs, const StructuredType *st, void *output, LargeInt numElements, int depth)
		{
			RDX_TRY(ctx)
			{
				LargeInt stride = st->_native.size;

				bool swap = IO::IFileStream::ShouldByteSwap();

				if(!swap)
				{
					LargeInt bulkSize = numElements * stride;
					if(fs->ReadBytes(output, bulkSize) != bulkSize)
						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
					return;
				}

				while(numElements--)
				{
					if(st->storageSpecifier == EnumValue::From(StructuredType::SS_Enum))
					{
						EnumValue v;
						if(fs->ReadSwappableBytes(&v, sizeof(EnumValue)) != sizeof(EnumValue))
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						*static_cast<EnumValue *>(output) = v;
					}
					else if(st->storageSpecifier == EnumValue::From(StructuredType::SS_Class) ||
						st->storageSpecifier == EnumValue::From(StructuredType::SS_RefStruct) ||
						st->storageSpecifier == EnumValue::From(StructuredType::SS_ValStruct))
					{
						// Parse out properties

						LargeInt nProperties = 0;
						LargeInt propertyOffset = 0;
						if(st->properties)
							nProperties = GCInfo::From(st->properties)->numElements;
						const Property *properties = st->properties;
						const LargeInt *offsets = st->_native.propertyOffsets;

						for(LargeInt i=0;i<nProperties;i++)
						{

#ifdef RDX_ENABLE_PADDED_BULK_SERIALIZE
							fs->Seek(offsets[i] - propertyOffset, IO::IFileStream::SEEK_Current);
							propertyOffset = offsets[i] + static_cast<const StructuredType *>(properties[i].type)->_native.size;
#endif
							void *propertyLocation = reinterpret_cast<UInt8*>(output) + offsets[i];
							RDX_PROTECT(ctx, DeserializeValue(ctx, objManager, pkg, domain, host, fs, false, properties[i].type, propertyLocation, depth + 1));
						}
						
#ifdef RDX_ENABLE_PADDED_BULK_SERIALIZE
						fs->Seek(st->_native.size - propertyOffset, IO::IFileStream::SEEK_Current);
#endif
					}
					else
					{
						RDX_STHROW(ctx, Errors::RDX_ERROR_INTERNAL_BAD_TYPE);
					}
				}
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}

		void DeserializeStructure(OperationContext *ctx, IObjectManager *objManager, Package *pkg,
			Domain domain, IPackageHost *host, IFileStream *fs, bool isText, const StructuredType *st, void *output, int depth)
		{
			bool isString;

			RDX_TRY(ctx)
			{
				if(depth >= RDX_MAX_RECURSION_DEPTH)
					RDX_STHROW(ctx, Errors::RDX_ERROR_RECURSION_LIMIT_EXCEEDED);

				if(st->_native.user.typeSerializer)
				{
					CRef<const String> str;

					if(isText)
					{
						RDX_PROTECT(ctx, st->_native.user.typeSerializer->DeserializeTextInstance(ctx, objManager, output, static_cast<ITextDeserializer*>(fs), host, pkg) );
					}
					else
					{
						RDX_PROTECT(ctx, st->_native.user.typeSerializer->DeserializeBinaryInstance(ctx, objManager, output, fs, host, pkg) );
					}
					return;
				}

				if(st->storageSpecifier == EnumValue::From(StructuredType::SS_Enum))
				{
					// Find the enum
					if(isText)
					{
						ITextDeserializer *tfp = static_cast<ITextDeserializer*>(fs);

						CRef<const String> str;
						RDX_PROTECT_ASSIGN(ctx, str, tfp->ParseToken(ctx, objManager, &isString, true) );
						if(!isString)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

						const Enumerant *enumerants = st->enumerants;
						LargeInt nEnums = GCInfo::From(enumerants)->numElements;

						for(LargeInt i=0;i<nEnums;i++)
						{
							if(enumerants[i].name == str)
							{
								*static_cast<EnumValue*>(output) = static_cast<EnumValue>(enumerants[i].value);
								return;
							}
						}
						RDX_STHROW(ctx, Errors::RDX_ERROR_UNRESOLVED_SYMBOL_REFERENCE);
					}
					else
					{
						EnumValue v;
						if(fs->ReadSwappableBytes(&v, sizeof(EnumValue)) != sizeof(EnumValue))
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

#ifdef RDX_VALIDATE_DESERIALIZED_ENUMS
						const Enumerant *enumerants = st->enumerants;
						LargeInt nEnums = GCInfo::From(enumerants)->numElements;

						for(LargeInt i=0;i<nEnums;i++)
						{
							if(enumerants[i].value == v)
							{
								*static_cast<EnumValue*>(output) = v;
								return;
							}
						}

						RDX_STHROW(ctx, Errors::RDX_ERROR_UNRESOLVED_SYMBOL_REFERENCE);
#else
						*static_cast<EnumValue*>(output) = v;
#endif
					}
				}

				const GCInfo *sti = GCInfo::From(st);

				// Parse out properties
				if(isText)
				{
					ITextDeserializer *tfp = static_cast<ITextDeserializer*>(fs);
					if(!tfp->CheckToken("{"))
						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
					RDX_PROTECT(ctx, tfp->SkipToken(ctx));

					while(!tfp->CheckToken("}"))
					{
						CRef<const String> propertyName;
						CRef<const String> propertyValue;
					
						RDX_PROTECT_ASSIGN(ctx, propertyName, tfp->ParseToken(ctx, objManager, &isString, true) );
					
						if(!tfp->CheckToken(":")) RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						RDX_PROTECT(ctx, tfp->SkipToken(ctx));

						// Figure out which property this is
						LargeInt propertyIndex = -1;
						LargeInt nProperties = 0;
						if(st->properties)
							nProperties = GCInfo::From(st->properties)->numElements;
						for(LargeInt i=0;i<nProperties;i++)
						{
							if(st->properties[i].name == propertyName)
							{
								propertyIndex = i;
								break;
							}
						}

						if(propertyIndex == -1)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

						Property *p = st->properties + propertyIndex;
						void *propertyLocation = reinterpret_cast<Byte*>(output) + st->_native.propertyOffsets[propertyIndex];

						RDX_PROTECT(ctx, DeserializeValue(ctx, objManager, pkg, domain, host, fs, isText, p->type, propertyLocation, depth + 1));

						if( tfp->CheckToken(",") )
							tfp->ParseToken(ctx, NULL, &isString, false);
						else if(!tfp->CheckToken("}"))
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
					}
					RDX_PROTECT(ctx, tfp->SkipToken(ctx) );
				}
				else
				{
					// Binary, all properties are serialized
					bool bulk = ((st->_native.flags & StructuredType::NativeProperties::STF_AllowBulkSerialize) != 0);

					if(bulk)
					{
						RDX_PROTECT(ctx, DeserializeBulk(ctx, objManager, pkg, domain, host, fs, st, output, 1, depth + 1));
					}
					else
					{
						LargeInt nProperties = 0;
						if(st->properties)
							nProperties = GCInfo::From(st->properties)->numElements;
						for(LargeInt i=0;i<nProperties;i++)
						{
							void *propertyLocation = reinterpret_cast<UInt8*>(output) + st->_native.propertyOffsets[i];
							Property *p = st->properties + i;
							RDX_PROTECT(ctx, DeserializeValue(ctx, objManager, pkg, domain, host, fs, isText, p->type, propertyLocation, depth + 1));
						}
					}
				}
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}

		void DeserializeValue(OperationContext *ctx, IObjectManager *objManager, Package *pkg,
			Domain domain, IPackageHost *host, IFileStream *fs, bool isText, const Type *t, void *propertyLocation, int depth)
		{
			RDX_TRY(ctx)
			{
				if(objManager->TypeIsObjectReference(t))
				{
					void *objRef;
					RDX_PROTECT(ctx, DeserializeReference(ctx, objManager, pkg, domain, host, fs, isText, t, &objRef) );
					if(objRef)
					{
						void *resolvedObjRef = objManager->ResolveShellableReference(objRef);
						if(resolvedObjRef && !objManager->ObjectCompatible(resolvedObjRef, t) )
						{
							const GCInfo *fromType = GCInfo::From(GCInfo::From(resolvedObjRef)->containerType);
							const GCInfo *toType = GCInfo::From(t);
							RDX_STHROW(ctx, Errors::RDX_ERROR_INCOMPATIBLE_CONVERSION);
						}
					}
					*static_cast<void**>(propertyLocation) = objRef;
				}
				else
					RDX_PROTECT(ctx, DeserializeStructure(ctx, objManager, pkg, domain, host, fs, isText, static_cast<const StructuredType*>(t), propertyLocation, depth) );
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}

		bool CanDeserializeObject(IObjectManager *objManager, Type *t, void *unloadedInstance)
		{
			StructuredType *containedStructure;

			if(t == objManager->GetBuiltIns()->st_Varying)
				return false;	// Can never deserialize varyings

			if(GCInfo::From(t)->containerType == objManager->GetBuiltIns()->st_ArrayOfType)
			{
				Type *subType = static_cast<ArrayOfType*>(t)->type;
				if(GCInfo::From(subType)->containerType == objManager->GetBuiltIns()->st_ArrayOfType)
					return true;
				else if(GCInfo::From(subType)->containerType == objManager->GetBuiltIns()->st_DelegateType)
					return true;
				else if(GCInfo::From(subType)->containerType == objManager->GetBuiltIns()->st_StructuredType)
				{
					containedStructure = static_cast<StructuredType*>(subType);

					switch(containedStructure->storageSpecifier.Value())
					{
					case StructuredType::SS_Class:
					case StructuredType::SS_Enum:
					case StructuredType::SS_Interface:
						return true;	// Dumb or ref types
					default:
						break;
					};
				}
				else
				{
					GCInfo *debugSubTypeGCI = GCInfo::From(subType);
					return false;	// Not a valid type
				}
			}
			else if(GCInfo::From(t)->containerType == objManager->GetBuiltIns()->st_StructuredType)
			{
				containedStructure = static_cast<StructuredType*>(t);
				const GCInfo *info = GCInfo::From(containedStructure);
				if(containedStructure->isAbstract)
					return false;	// Can't deserialize abstract types
			}
			else
				return false;	// Not a valid type

			
			if(!(containedStructure->_native.flags & StructuredType::NativeProperties::STF_StructureEvaluated))
				return false;	// Don't have the structure layout

			if(containedStructure->_native.flags & StructuredType::NativeProperties::STF_DefaultEvaluated)
				return true;
			if( (containedStructure->_native.flags & StructuredType::NativeProperties::STF_DependencyDefaultsEvaluated)
				&& containedStructure->defaultValue == unloadedInstance)
				return true;	// This is the default and it's being loaded, go ahead
			return false;		// Can't determine the default yet
		}

		bool DeserializeObject(OperationContext *ctx, IObjectManager *objManager, Package *pkg,
			Package::ManifestLocal *mfl, Domain domain, IPackageHost *host, IO::IFileStream *stream, bool isText)
		{
			CRef<void> obj;

			CRef<Type> objType = mfl->resolvedType;

			RDX_TRY(ctx)
			{
				if(!objType)
					RDX_STHROW(ctx, Errors::RDX_ERROR_UNRESOLVED_SYMBOL_REFERENCE);

				IFileStream *streamToUse;
				TextFileParser textParser(stream);
				if(isText)
					streamToUse = &textParser;
				else
					streamToUse = stream;
				streamToUse->Seek(mfl->fileOffset, IFileStream::SEEK_Start);


				if(!CanDeserializeObject(objManager, objType, mfl->ref.InterpretAs<void>()))
					return false;

				if(GCInfo::From(objType)->containerType == objManager->GetBuiltIns()->st_StructuredType)
				{
					StructuredType *objST = objType.Cast<StructuredType>();

					RDX_PROTECT_ASSIGN(ctx, obj, objManager->CreateContainer(ctx, objST->_native.size, 1, 0, mfl->domain, objST, NULL, 0) );
					if(domain != DOMAIN_Runtime && domain != DOMAIN_Duplicable)
						GCInfo::From(obj)->package = pkg;

					if(!(mfl->flags & Package::ManifestLocal::PMLF_Anonymous) && !(objST->_native.flags & StructuredType::NativeProperties::STF_StructureIsMutable))
						pkg->_native.persistence = Package::PERS_Persistent;

					if(mfl->domain == DOMAIN_Duplicable)
					{
						if((objST->_native.user.flags & StructuredType::NativeProperties::UserProperties::STUF_AllowDuplicates) == 0)
						{
							const GCInfo *objSTI = GCInfo::From(objST);
							RDX_STHROW(ctx, Errors::RDX_ERROR_FORBIDDEN_SYMBOL_DUPLICATION);
						}
					}

					// If this is a non-reference type and the resource is non-constant, then the resource is mutable even if the fields are constant, so this package is non-persistent
					if(!objManager->TypeIsObjectReference(objST) && !(mfl->flags & Package::ManifestLocal::PMLF_Constant))
						pkg->_native.persistence = Package::PERS_Persistent;

					if(mfl->flags & Package::ManifestLocal::PMLF_Constant)
						GCInfo::From(obj)->gcflags |= GCInfo::GCOF_ConstantStructure;
					
					RDX_PROTECT(ctx, objManager->AddUnloadedObject(ctx, obj));
					RDX_PROTECT(ctx, DeserializeStructure(ctx, objManager, pkg, domain, host, streamToUse, isText, objST, obj, 0) );
				}
				else if(GCInfo::From(objType)->containerType == objManager->GetBuiltIns()->st_ArrayOfType)
				{
					ArrayOfType *objAOT = objType.Cast<ArrayOfType>();

					if(!objAOT->isConstant)
						pkg->_native.persistence = Package::PERS_Persistent;

					if(mfl->domain == DOMAIN_Duplicable)
					{
						// Duplicable arrays must be constant
						if(objAOT->isConstant == FalseValue)
							RDX_STHROW(ctx, Errors::RDX_ERROR_FORBIDDEN_SYMBOL_DUPLICATION);
					}
					RDX_PROTECT(ctx, DeserializeArray(ctx, objManager, pkg, mfl->domain, host, streamToUse, isText, objType.Cast<ArrayOfType>(), obj) );
					
					RDX_PROTECT_ASSIGN(ctx, mfl->ref.resolution, obj);
				}
				else
					RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

				mfl->ref.resolution = obj;

				return true;
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROWV(ctx, false);
			}
			RDX_ENDTRY
		}

		CRef<Package> DeserializePackage(OperationContext *ctx, IObjectManager *objManager, Domain domain,
			IPackageHost *host, IO::IFileStream *stream, bool isText)
		{
			CRef<Package> pkg = NULL;
			RDX_TRY(ctx)
			{
				RDX_PROTECT_ASSIGN(ctx, pkg, objManager->CreateObject<Package>(ctx, NULL, &PackageProcessor::instance));
				pkg->_native.domain = domain;

				if(isText)
					RDX_PROTECT(ctx, ParseTextManifest(ctx, objManager, stream, pkg) );
				else
					RDX_PROTECT(ctx, ParseBinaryManifest(ctx, objManager, stream, pkg) );

				// Determine the domain of all local symbols in this package
				if(pkg->_native.localSymbols)
				{
					LargeInt nls = GCInfo::From(pkg->_native.localSymbols)->numElements;
					for(LargeInt i=0;i<nls;i++)
					{
						Domain symbolDomain = DOMAIN_Runtime;
						Package::ManifestLocal *ml = pkg->_native.localSymbols + i;
						if(ml->symbolName && !(ml->flags & Package::ManifestLocal::PMLF_Anonymous))
						{
							// Not an anonymous object, this needs to actually be storable in this package
							symbolDomain = host->DomainForSymbolName(objManager, ml->symbolName, domain);
							if(symbolDomain != DOMAIN_Duplicable && symbolDomain != domain)
								RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						}

						ml->domain = symbolDomain;
					}
				}
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROWV(ctx, NULL);
			}
			RDX_ENDTRY

			return pkg;
		}

		// =======================================================================================
		// Writing
		void WriteReference(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const void *obj, bool resTag);

		void WriteHeader(IObjectManager *objm, IO::IFileStream *fs, bool isText, LargeInt numLocalSymbols, LargeInt numImports, LargeInt numStrings)
		{
			if(!isText)
			{
				const char *magic = RDX_MAGIC;
				fs->WriteBytes(magic, 16);
				fs->WriteConverted<LargeInt, BinPackageLargeInt>(numImports);
				fs->WriteConverted<LargeInt, BinPackageLargeInt>(numLocalSymbols);
				fs->WriteConverted<LargeInt, BinPackageLargeInt>(numStrings);
			}
		}

		void WriteImport(OperationContext *ctx, IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const String *symbolName)
		{
			// Serialize name
			if(isText)
			{
				fs->WriteBytes("import ", 7);
				objm->SerializeTextString(symbolName, fs);
				fs->WriteBytes("\n", 1);
			}
			else
				objm->SerializeBinaryString(symbolName, fs, ssidTable);
		}

		void ReserveObjectExport(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, void *obj)
		{
			const ITypeSerializer *strs = objm->GetBuiltIns()->st_String->_native.user.typeSerializer;

			if(!isText)
			{
				GCInfo *info = GCInfo::From(obj);

				BinPackageLargeInt offset = 0;
				fs->WriteBytes(&offset, sizeof(BinPackageLargeInt));

				objm->SerializeBinaryString(info->gstSymbol, fs, ssidTable);
				{
					const StaticLookupPODKey<const GCInfo *> containerInfoKey(GCInfo::From(info->containerType));
					fs->WriteConverted<LargeInt, BinPackageLargeInt>(*(ssidTable->Lookup(containerInfoKey)));
				}

				UInt32 flags = 0;
				fs->WriteSwappableBytes(&flags, sizeof(flags));
			}
		}

		void WriteObjectExport(IObjectManager *objm, IO::IFileStream *fs, bool isText, LargeInt *catalogOffset, LargeInt objectOffset, const void *obj)
		{
			if(!isText)
			{
				LargeInt resumeLoc = fs->Tell();
				fs->Seek(*catalogOffset, IFileStream::SEEK_Start);
				fs->WriteConverted<LargeInt, BinPackageLargeInt>(objectOffset);
				// Offset, symbol, SSID, flags
				(*catalogOffset) += sizeof(BinPackageLargeInt) + sizeof(BinPackageLargeInt) + sizeof(BinPackageLargeInt) + sizeof(UInt32);
				fs->Seek(resumeLoc, IFileStream::SEEK_Start);
			}
		}
		
		void WriteReference(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const void *obj, bool resTag)
		{
			const ITypeSerializer *strs = objm->GetBuiltIns()->st_String->_native.user.typeSerializer;
			if(isText)
			{
				if(obj)
				{
					const GCInfo *info = GCInfo::From(obj);
					if(info->containerType == objm->GetBuiltIns()->st_String)
						objm->SerializeTextString(static_cast<const String*>(obj), fs);
					else
					{
						if(resTag)
							fs->WriteBytes(" res ", 5);
						
						if(info->gstSymbol)
							objm->SerializeTextString(info->gstSymbol, fs);
						else
						{
							const ITypeSerializer *lis = objm->GetBuiltIns()->st_LargeInt->_native.user.typeSerializer;
							fs->WriteBytes("'#%%RuntimeSymbol", 17);
							StaticLookupPODKey<const GCInfo *> infoKey(info);
							lis->SerializeTextInstance(objm, ssidTable->Lookup(infoKey), fs, ssidTable);
							fs->WriteBytes("'", 1);
						}
					}
				}
				else
					fs->WriteBytes(" null", 5);
			}
			else
			{
				if(obj)
				{
					StaticLookupPODKey<const GCInfo *> infoKey(GCInfo::From(obj));
					fs->WriteConverted<LargeInt, BinPackageLargeInt>(*(ssidTable->Lookup(infoKey)));
				}
				else
				{
					BinPackageLargeInt i = 0;
					fs->WriteSwappableBytes(&i, sizeof(BinPackageLargeInt));
				}
			}
		}

		void WriteStructure(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const StructuredType *st, const void *obj, int depth);

		void WriteValue(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const Type *t, const void *obj, int depth)
		{
			if(objm->TypeIsObjectReference(t))
				WriteReference(objm, fs, isText, ssidTable, *static_cast<void*const*>(obj), true);
			else
				WriteStructure(objm, fs, isText, ssidTable, static_cast<const StructuredType*>(t), obj, depth);
		}

		void WriteValue(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const Type *t, const void *obj)
		{
			WriteValue(objm, fs, isText, ssidTable, t, obj, 0);
		}

		void WriteBulk(IObjectManager *objm, IO::IFileStream *fs, const SSIDTable *ssidTable, const StructuredType *st, LargeInt count, const void *obj, int depth)
		{
			LargeInt stride = st->_native.size;
			bool shouldSwap = IO::IFileStream::ShouldByteSwap();

			if(!IO::IFileStream::ShouldByteSwap())
			{
				fs->WriteBytes(obj, stride * count);
				return;
			}

			UInt8 zeroPad = 0;

			// Target is padded and byte swapped
			const Property *properties = st->properties;
			const LargeInt *offsets = st->_native.propertyOffsets;

			LargeInt nProperties = 0;
			if(st->properties)
				nProperties = GCInfo::From(properties)->numElements;

			while(count--)
			{
				if(st->storageSpecifier == EnumValue::From(StructuredType::SS_Enum))
				{
					fs->WriteSwappedBytes(obj, sizeof(EnumValue));
				}
				else if(st->storageSpecifier == EnumValue::From(StructuredType::SS_Class) ||
					st->storageSpecifier == EnumValue::From(StructuredType::SS_RefStruct) ||
					st->storageSpecifier == EnumValue::From(StructuredType::SS_ValStruct))
				{
					LargeInt propertyOffset = 0;
					for(LargeInt i=0;i<nProperties;i++)
					{
	#ifdef RDX_ENABLE_PADDED_BULK_SERIALIZE
						while(propertyOffset < offsets[i])
						{
							fs->WriteBytes(&zeroPad, 1);
							propertyOffset++;
						}
						propertyOffset += static_cast<const StructuredType *>(properties[i].type)->_native.size;
	#endif
						WriteValue(objm, fs, false, ssidTable, properties[i].type, static_cast<const UInt8 *>(obj) + offsets[i], depth);
					}

	#ifdef RDX_ENABLE_PADDED_BULK_SERIALIZE
					while(propertyOffset < stride)
					{
						fs->WriteBytes(&zeroPad, 1);
						propertyOffset++;
					}
	#endif

					obj = static_cast<const UInt8 *>(obj) + stride;
				}
				else
					fs->Abort();
			}
		}

		void WriteStructure(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const StructuredType *st, const void *obj, int depth)
		{
			const ITypeSerializer *strs = objm->GetBuiltIns()->st_String->_native.user.typeSerializer;
			if(depth > RDX_MAX_RECURSION_DEPTH)
			{
				fs->Abort();
				return;
			}

			if(st->_native.user.typeSerializer)
			{
				if(isText)
				{
					st->_native.user.typeSerializer->SerializeTextInstance(objm, obj, fs, ssidTable);
				}
				else
				{
					st->_native.user.typeSerializer->SerializeBinaryInstance(objm, obj, fs, ssidTable);
				}
				return;
			}

			if(st->storageSpecifier == EnumValue::From(StructuredType::SS_Enum))
			{
				// Find the enum
				if(isText)
				{
					const Enumerant *enumerants = st->enumerants;
					LargeInt nEnums = GCInfo::From(enumerants)->numElements;
					EnumValue v = *static_cast<const EnumValue*>(obj);
					const String *s = NULL;

					for(LargeInt i=0;i<nEnums;i++)
					{
						if(enumerants[i].value == v)
						{
							s = enumerants[i].name;
							break;
						}
					}

					objm->SerializeTextString(s, fs);

					return;
				}
				else
				{
					fs->WriteSwappableBytes(obj, sizeof(EnumValue)); 
					return;
				}
			}

			const GCInfo *sti = GCInfo::From(st);

			// Write properties
			if(isText)
			{
				fs->WriteBytes("{\n", 2);
			}
			
			LargeInt nProperties = 0;
			if(st->properties)
				nProperties = GCInfo::From(st->properties)->numElements;

			if(isText)
			{
				for(LargeInt i=0;i<nProperties;i++)
				{
					objm->SerializeTextString(st->properties[i].name, fs);
					fs->WriteBytes(" : ", 3);

					Property *p = st->properties + i;
					const void *propertyLocation = reinterpret_cast<const UInt8*>(obj) + st->_native.propertyOffsets[i];

					WriteValue(objm, fs, isText, ssidTable, p->type, propertyLocation, depth + 1);

					fs->WriteBytes(",\n", 2);
				}
				fs->WriteBytes("}\n", 2);
			}
			else
			{
				bool bulk = ((st->_native.flags & StructuredType::NativeProperties::STF_AllowBulkSerialize) != 0);

				if(bulk)
				{
					WriteBulk(objm, fs, ssidTable, st, 1, obj, depth + 1);
				}
				else
				{
					const Property *properties = st->properties;
					const LargeInt *offsets = st->_native.propertyOffsets;

					for(LargeInt i=0;i<nProperties;i++)
					{
						const void *propertyLocation = reinterpret_cast<const UInt8*>(obj) + offsets[i];
						WriteValue(objm, fs, isText, ssidTable, properties[i].type, propertyLocation, depth + 1);
					}
				}
			}
		}

		void WriteArray(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const ArrayOfType *aot, const void *obj)
		{
			const ITypeSerializer *lis = objm->GetBuiltIns()->st_LargeInt->_native.user.typeSerializer;
			for(LargeInt i=0;i<aot->numDimensions;i++)
			{
				LargeInt d = GCInfo::From(obj)->dimensions[i];

				if(isText)
				{
					lis->SerializeTextInstance(objm, &d, fs, ssidTable);
					fs->WriteBytes(" ", 1);
				}
				else
				{
					lis->SerializeBinaryInstance(objm, &d, fs, ssidTable);
				}
			}

			const Type *subType = aot->type;
			LargeInt elementSize = objm->TypeElementSize(aot);

			const UInt8 *byteBase = reinterpret_cast<const UInt8*>(obj);
				
			// Write out elements
			if(isText)
				fs->WriteBytes("{\n", 2);

			LargeInt totalElements = GCInfo::From(obj)->numElements;
			if(isText == false
				&& (GCInfo::From(subType)->containerType == objm->GetBuiltIns()->st_StructuredType)
				&& (static_cast<const StructuredType *>(subType)->_native.flags & StructuredType::NativeProperties::STF_AllowBulkSerialize)
				&& !objm->TypeIsObjectReference(subType))
			{
				// Bulk serializable values
				WriteBulk(objm, fs, ssidTable, static_cast<const StructuredType *>(subType), totalElements, byteBase, 0);
			}
			else
			{
				for(LargeInt i=0;i<totalElements;i++)
				{
					WriteValue(objm, fs, isText, ssidTable, subType, byteBase, 0);
					byteBase += elementSize;

					if(isText)
						fs->WriteBytes(",\n", 2);
				}
			}
			if(isText)
				fs->WriteBytes("}", 1);
		}

		void WriteObject(IObjectManager *objm, IO::IFileStream *fs, bool isText, const SSIDTable *ssidTable, const void *obj)
		{
			const GCInfo *objInfo = GCInfo::From(obj);
			const Type * objType = objInfo->containerType;

			bool isAnonymous = (objInfo->gstSymbol == NULL);

			if(isText)
			{
				fs->WriteBytes("\n\ndef ", 6);
				WriteReference(objm, fs, isText, ssidTable, obj, false);
				fs->WriteBytes(" ", 1);
				if(isAnonymous)
					fs->WriteBytes("anonymous ", 10);
				if(objInfo->gcflags & GCInfo::GCOF_ConstantStructure)
					fs->WriteBytes("const ", 6);
				WriteReference(objm, fs, isText, ssidTable, objType, false);
				fs->WriteBytes(" ", 1);
			}

			if(GCInfo::From(objType)->containerType == objm->GetBuiltIns()->st_StructuredType)
			{
				const StructuredType *objST = static_cast<const StructuredType*>(objType);

				WriteStructure(objm, fs, isText, ssidTable, objST, obj, 0);
			}
			else if(GCInfo::From(objType)->containerType == objm->GetBuiltIns()->st_ArrayOfType)
			{
				WriteArray(objm, fs, isText, ssidTable, static_cast<const ArrayOfType*>(objType), obj);
			}
			else
				fs->Abort();	// Don't know what happened, but this shouldn't be allowed
		}
	};
}
